package org.xbmc.net;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.Socket;
import java.net.URL;
import java.net.URLEncoder;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.HTTP;

import android.util.Log;


public class RestClient {

	private final String TAG = RestClient.class.getSimpleName();

	private ArrayList<NameValuePair> params;
	private ArrayList<NameValuePair> headers;
	private String url;
	private String response;
	private String message;
	private int responseCode;

	private boolean isHttps = false;

	public RestClient(String url) {
		this.url = url;
		this.params = new ArrayList<NameValuePair>();
		this.headers = new ArrayList<NameValuePair>();
	}

	public void addParams(String name, String value) {
		this.params.add(new BasicNameValuePair(name, value));
	}

	public void addHeader(String name, String value) {
		this.headers.add(new BasicNameValuePair(name, value));
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public void setHttps(boolean b) {
		this.isHttps = b;
	}

	public String getResponse() {
		return response;
	}

	public void setResponse(String response) {
		this.response = response;
	}

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}

	public int getResponseCode() {
		return responseCode;
	}

	public void setResponseCode(int responseCode) {
		this.responseCode = responseCode;
	}

	public void excuteWithHttpUrlConnection() {
		URL mURL;
		HttpURLConnection con = null;
		InputStream in = null;
		try {
			String combinedParams = "";
			if (!params.isEmpty()) {
				combinedParams += "&";
				for (NameValuePair p : params) {
					if (p == null)
						return;
					if (p.getValue() == null)
						return;

					String paramString = p.getName() + "=" + URLEncoder.encode(p.getValue(), "UTF-8");
					if (combinedParams.length() > 1) {
						combinedParams += "&" + paramString;
					} else {
						combinedParams += paramString;
					}
				}
			}

			mURL = new URL(url + combinedParams);
			con = (HttpURLConnection) mURL.openConnection();
			in = con.getInputStream();
			BufferedReader reader = new BufferedReader(new InputStreamReader(in));
			String line = "";
			StringBuilder mStringBuilder = new StringBuilder();
			while ((line = reader.readLine()) != null) {
				mStringBuilder.append(line);
			}
			response = mStringBuilder.toString();

		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			if (in != null) {
				try {
					in.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			if (con != null) {
				con.disconnect();
			}

		}
	}

	public void excuteWithHttpUrlConnection(int timeout) {
		URL mURL;
		HttpURLConnection con = null;
		InputStream in = null;
		try {
			String combinedParams = "";
			if (!params.isEmpty()) {
				combinedParams += "&";
				for (NameValuePair p : params) {
					if (p == null)
						return;
					if (p.getValue() == null)
						return;

					String paramString = p.getName() + "=" + URLEncoder.encode(p.getValue(), "UTF-8");
					if (combinedParams.length() > 1) {
						combinedParams += "&" + paramString;
					} else {
						combinedParams += paramString;
					}
				}
			}

			mURL = new URL(url + combinedParams);
			con = (HttpURLConnection) mURL.openConnection();
			con.setConnectTimeout(timeout);
			con.setReadTimeout(timeout);
			in = con.getInputStream();
			BufferedReader reader = new BufferedReader(new InputStreamReader(in,"UTF-8"));
			String line = "";
			StringBuilder mStringBuilder = new StringBuilder();
			while ((line = reader.readLine()) != null) {
				mStringBuilder.append(line);
			}
			response = mStringBuilder.toString();

		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			if (in != null) {
				try {
					in.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			if (con != null) {
				con.disconnect();
			}

		}
	}

	public void excute(RequestMethod resMethod) throws UnsupportedEncodingException {
		switch (resMethod) {
		case GET:
			// add parameters
			HttpGet requestGet = null;
			String combinedParams = "";
			if (!params.isEmpty()) {
				combinedParams += "&";
				for (NameValuePair p : params) {
					if (p == null)
						return;
					if (p.getValue() == null)
						return;

					String paramString = p.getName() + "=" + URLEncoder.encode(p.getValue(), "UTF-8");
					if (combinedParams.length() > 1) {
						combinedParams += "&" + paramString;
					} else {
						combinedParams += paramString;
					}
				}
				requestGet = new HttpGet(url + combinedParams);
				// add headers

				Log.d(TAG, "---->URL=" + (url + combinedParams));
			} else {
				requestGet = new HttpGet(url);
				Log.d(TAG, "---->URL=" + (url));
			}
			for (NameValuePair h : headers) {
				requestGet.addHeader(h.getName(), h.getValue());
			}
			executeRequest(requestGet);
			break;
		case POST:
			HttpPost requestPost = new HttpPost(url);
			// add headers
			for (NameValuePair h : headers) {
				requestPost.addHeader(h.getName(), h.getValue());
			}
			if (!params.isEmpty()) {
				requestPost.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8));
			}
			executeRequest(requestPost);
			break;
		case PUT:
			HttpPut requestPut = new HttpPut(url);
			// add headers
			for (NameValuePair h : headers) {
				requestPut.addHeader(h.getName(), h.getValue());
			}
			if (!params.isEmpty()) {
				requestPut.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8));
			}
			HttpClient client = new DefaultHttpClient();
			try {
				HttpResponse httpResponse = client.execute(requestPut);
				HttpEntity entity = httpResponse.getEntity();
				if (entity != null) {
					InputStream instream = entity.getContent();
					response = convertStreamToString(instream);
					// Closing the input stream will trigger connection release
					instream.close();
				}
			} catch (ClientProtocolException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			// executeRequest(requestPut);
			break;
		default:
			break;
		}
	}

	public void executeRequest(HttpUriRequest request) {
		HttpClient client;
		if (isHttps) {
			client = getNewHttpClient();
		} else {
			client = new DefaultHttpClient();
		}
		// TODO hung.pd chinh ket noi nhanh hon
		HttpParams httpParameters = client.getParams();
		// HttpConnectionParams.setConnectionTimeout(httpParameters,
		// HttpConnectionParams.CONNECTION_TIMEOUT);
		// HttpConnectionParams.setSoTimeout(httpParameters,
		// WAIT_RESPONSE_TIMEOUT);
		HttpConnectionParams.setTcpNoDelay(httpParameters, true);

		HttpResponse httpResponse;
		try {
			request.setHeader("Cache-Control", "no-cache");
			httpResponse = client.execute(request);
			responseCode = httpResponse.getStatusLine().getStatusCode();
			message = httpResponse.getStatusLine().getReasonPhrase();

			HttpEntity entity = httpResponse.getEntity();
			if (entity != null) {
				InputStream instream = entity.getContent();
				response = convertStreamToString(instream);
				// Closing the input stream will trigger connection release
				instream.close();
			}

		} catch (ClientProtocolException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			client.getConnectionManager().shutdown();
		}

	}

	private String convertStreamToString(InputStream is) {
		BufferedReader reader = new BufferedReader(new InputStreamReader(is));
		StringBuilder sb = new StringBuilder();
		String line = null;
		try {
			while ((line = reader.readLine()) != null) {
				sb.append(line + "\n");
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				is.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return sb.toString();
	}

	public enum RequestMethod {
		GET, POST, PUT
	}

	public class MySSLSocketFactory extends SSLSocketFactory {
		SSLContext sslContext = SSLContext.getInstance("TLS");

		public MySSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException,
				KeyStoreException, UnrecoverableKeyException {
			super(truststore);

			TrustManager tm = new X509TrustManager() {
				public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
				}

				public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
				}

				public X509Certificate[] getAcceptedIssuers() {
					return null;
				}
			};

			sslContext.init(null, new TrustManager[] { tm }, new java.security.SecureRandom());
			HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
		}

		@Override
		public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException,
				UnknownHostException {
			return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
		}

		@Override
		public Socket createSocket() throws IOException {
			return sslContext.getSocketFactory().createSocket();
		}
	}

	public HttpClient getNewHttpClient() {
		try {
			KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
			trustStore.load(null, null);

			SSLSocketFactory sf = new MySSLSocketFactory(trustStore);
			sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

			HttpParams params = new BasicHttpParams();
			HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
			HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);

			SchemeRegistry registry = new SchemeRegistry();
			registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
			registry.register(new Scheme("https", sf, 443));

			ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);

			return new DefaultHttpClient(ccm, params);
		} catch (Exception e) {
			return new DefaultHttpClient();
		}
	}

}
